#include "ParticleSystemSceneNode.h"
#include "ParticleEmitterBox.h"
#include "include/ITimer.h"
#include "include/ICameraSceneNode.h"
#include "D3DTexture.h"

namespace ISNW
{
namespace scene
{
	void ParticleSystemSceneNode::setEmitter(IParticleEmitter* e)
	{
		if(emitter)
		{
			emitter->drop();
		}

		emitter = e;
		
		if(e)
		{
			emitter->grab();
		}
	}

	IParticleEmitter* ParticleSystemSceneNode::createBoxEmitter(core::aabbox3df box,core::vector3df dir,
		u32 MPPS,u32 mpps,u32 MCOLOR,u32 mcolor, u32 angle,u32 life)
	{
		IParticleEmitter* em = new ParticleEmitterBox(box,dir,MPPS,mpps,MCOLOR,MCOLOR,angle,life);

		return em;
	}

	void ParticleSystemSceneNode::render()
	{
		if(!isVisible || !scenemanager) return;

		core::matrix4f mat = scenemanager->getActiveCamera()->getFrustum().Matrices[VIEW_MATRIX];

		f32 w = ParticleSize.width * 0.5f;
		core::vector3df horizon(mat[0]*w,mat[4]*w,mat[8]*w);
		f32 h = ParticleSize.height * 0.5f;
		core::vector3df vetical(mat[1]*h,mat[5]*h,mat[9]*h);

		core::vector3df view(-mat[2],-mat[6],-mat[10]);

		buffer.vertex_buffer.set_used(Particles.size()*4);
		buffer.indices16_buffer.set_used(Particles.size()*6);

		for(u32 i = 0; i < Particles.size(); ++i)
		{
			u32 k = 4 * i;

			buffer.vertex_buffer[k+0].pos = Particles[i].position + horizon + vetical;
			buffer.vertex_buffer[k+0].color = Particles[i].color;
			buffer.vertex_buffer[k+0].coords = core::vector2df(0.35,0.35);
			buffer.vertex_buffer[k+0].normal = view;

			buffer.vertex_buffer[k+1].pos = Particles[i].position + horizon - vetical;
			buffer.vertex_buffer[k+1].color = Particles[i].color;
			buffer.vertex_buffer[k+1].coords = core::vector2df(0.35,0.65);
			buffer.vertex_buffer[k+1].normal = view;

			buffer.vertex_buffer[k+2].pos = Particles[i].position - horizon - vetical;
			buffer.vertex_buffer[k+2].color = Particles[i].color;
			buffer.vertex_buffer[k+2].coords = core::vector2df(0.65,0.65);
			buffer.vertex_buffer[k+2].normal = view;

			buffer.vertex_buffer[k+3].pos = Particles[i].position - horizon + vetical;
			buffer.vertex_buffer[k+3].color = Particles[i].color;
			buffer.vertex_buffer[k+3].coords = core::vector2df(0.65,0.35);
			buffer.vertex_buffer[k+3].normal = view;

			u32 l = 6 * i;

			buffer.indices16_buffer[l+0] = k+0;
			buffer.indices16_buffer[l+1] = k+1;
			buffer.indices16_buffer[l+2] = k+2;

			buffer.indices16_buffer[l+3] = k+0;
			buffer.indices16_buffer[l+4] = k+2;
			buffer.indices16_buffer[l+5] = k+3;
		}

		{
			if(buffer.m_material.texturelayers[0].texture == 0)
			{
				buffer.m_material.texturelayers[0].texture = new video::D3DTexture;
				buffer.m_material.texturelayers[0].texture->filename = "fireball.bmp";
			}
		}

		scenemanager->getdriver()->setmaterial(buffer.m_material);

		scenemanager->getdriver()->setTransform(video::E_WORLD,AbsoluteTransformation);

		scenemanager->getdriver()->drawtriangles(buffer.vertex_buffer.pointer(),video::STANDAND_VERTEX,buffer.vertex_buffer.size(),buffer.indices16_buffer.pointer(),buffer.indices16_buffer.size()/3);
	}

	void ParticleSystemSceneNode::doParticleSystem(u32 Time)
	{
		if(lastTime == 0)
		{
			lastTime = Time;
			return;
		}

		if(scenemanager == 0) return;

		u32 time_escaped = Time - lastTime;
		lastTime = Time;

		// emitters
		u32 old_size = Particles.size();

		if(emitter && old_size < 20000)
		{
			Particle* pointer = 0;
			u32 count = 0;
			count = emitter->emitt(Time,time_escaped,pointer);

			if(!count || !pointer) return;


			{
				if(count + old_size > 20000)
				{
					count = 20000 - old_size;
				}
			}

			Particles.set_used(old_size+count);

			for(u32 i = 0; i < count; ++i)
			{
				Particles[old_size+i] = pointer[i];
				Particles[old_size+i].velocity = pointer[i].startVelocity;
			}
		}

		// affectors
		{
			core::list<IParticleAffector*>::iterator it = Affectors.begin();
			for(;it != Affectors.end(); ++it)
			{
				(*it)->affect(Particles.pointer(),Particles.size(),Time);
			}
		}

		for(u32 i = 0; i < Particles.size();++i)
		{
			if(Time >= Particles[i].endTime)
			{
				Particles.erase(i);
				--i;
			}
		}
		for(u32 i = 0; i < Particles.size(); ++i)
		{
			{
				Particles[i].position += Particles[i].velocity * (f32)time_escaped * 0.001f;
			}
		}
	}

	void ParticleSystemSceneNode::onRegisterSceneNode()
	{
		if(!isVisible) return;

		doParticleSystem(os::Timer::getTime());

		if(scenemanager)
		{
			scenemanager->registernodeforrendering(this,E_RT_DEFAULT);
		}
	}

	void ParticleSystemSceneNode::setMaterial(video::material m)
	{
		buffer.m_material = m;
	}

} // end namespace scene
} // end namespace ISNW